package com.smart.tool.transaction;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * @Author gao.gwq
 * @Version TransactionUtils.java, v 0.1 2024年01月25日 15:05 gao.gwq
 * @Description: TODO 编程事务的通用方法
 * 编程式事务 具有灵活性高，可控性强，适应复杂的业务系统，在业务系统中，事务的控制建议使用编程式事务
 *
 *<p>
 * 事务的通用方法 example
 * TransactionUtils.execute(transactionTemplate, new Action<String>() {
 * @Override
 * public String doAction(TransactionContext<String> transactionContext) {
 * return "success";
 * }
 *
 * @Override
 * public String processError(TransactionContext<String> transactionContext, Throwable throwable) {
 * return "error";
 * }
 * @Override
 * public void afterCommit(TransactionContext<String> transactionContext) {
 * }
 * });
 * </p>
 */
public class TransactionUtils {


    public static <T> T execute(TransactionTemplate transactionTemplate, Action<T> action) {

        return transactionTemplate.execute(new TransactionCallback<T>() {
            @Override
            public T doInTransaction(TransactionStatus status) {
                TransactionContext<T> transactionContext = new TransactionContext(status,TransactionSynchronizationManager.getCurrentTransactionName());
                try {
                    registerTrancationAdapter(transactionContext, action);
                    T result = action.doAction(transactionContext);
                    transactionContext.setResult(result);
                    return result;
                } catch (RuntimeException throwable) {
                    return processRollback(status, transactionContext, throwable, action);
                } catch (Error throwable) {
                    return processRollback(status, transactionContext, throwable, action);
                } catch (Throwable throwable) {
                    return processRollback(status, transactionContext, throwable, action);
                }
            }
        });
    }

    /**
     * 异常处理
     *
     * @param status
     * @param transactionContext
     * @param throwable
     * @param action
     * @param <T>
     * @return
     */
    private static <T> T processRollback(TransactionStatus status, TransactionContext<T> transactionContext, Throwable throwable,

        Action<T> action) {
        transactionContext.setThrowable(throwable);
        transactionContext.setSucess(false);
        status.setRollbackOnly();
        return action.processError(transactionContext, throwable);
    }


    public static <T> void executeWithOutResult(TransactionTemplate transactionTemplate, ActionWithOutResult action) {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(TransactionStatus status) {

                TransactionContext<T> transactionContext = new TransactionContext(status,
                    TransactionSynchronizationManager.getCurrentTransactionName());
                try {
                    registerTrancationAdapter(transactionContext, action);
                    action.doAction(transactionContext);
                } catch (RuntimeException throwable) {
                    processRollback(status, transactionContext, throwable, action);
                } catch (Error throwable) {
                    processRollback(status, transactionContext, throwable, action);
                } catch (Throwable throwable) {
                    processRollback(status, transactionContext, throwable, action);
                }
            }
        });
    }

    /**
     * 异常处理
     *
     * @param status
     * @param transactionContext
     * @param throwable
     * @param action
     * @return
     */
    private static <T> void processRollback(TransactionStatus status, TransactionContext<T> transactionContext, Throwable throwable,

        ActionWithOutResult action) {

        transactionContext.setThrowable(throwable);
        transactionContext.setSucess(false);
        status.setRollbackOnly();
        action.processError(transactionContext, throwable);
    }

    /**
     * 注册事务监听
     *
     * @param transactionContext
     * @param action
     */
    public static <T> void  registerTrancationAdapter(TransactionContext<T> transactionContext, BaseAction action) {

        //事务commit 后执行
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                action.afterCommit(transactionContext);
            }

            @Override
            public void afterCompletion(int status) {
                transactionContext.setStatus(status);
                action.afterCompletion(transactionContext);
            }

            @Override
            public void beforeCommit(boolean readOnly) {
                transactionContext.setReadOnly(readOnly);
                action.beforeCommit(transactionContext);
            }

            @Override
            public void beforeCompletion() {
                action.beforeCompletion(transactionContext);
            }
        });
    }

}
